<?php

/*
 * Copyright (c) 2012-2013 Aalto University and University of Helsinki
 * MIT License
 * see LICENSE.txt for more information
 */

require_once('controller/Controller.php');

class WebController extends Controller {
  public $twig;
  public $path_fix;
  public $parts;
  public $request_uri;

  public function __construct($path_fix) {
    parent::__construct();
    
    // used for making proper hrefs for the language selection
    $this->request_uri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    $this->parts = rtrim($_SERVER['HTTP_HOST'] . $_SERVER['SCRIPT_NAME'], 'php.xedni');
    $this->path_fix = $path_fix;

    // Specify the location of the translation tables
    bindtextdomain('onkiLight', 'resource/translations');
    bind_textdomain_codeset('onkiLight', 'UTF-8');

    // Choose domain for translations
    textdomain('onkiLight');
    
    // initialize Twig templates
    $tmp_dir = TEMPLATE_CACHE;
    
    // check if the cache pointed by config.inc exists, if not we create it.
    if (!file_exists($tmp_dir)) mkdir($tmp_dir);

    // specify where to look for templates and cache
    $loader = new Twig_Loader_Filesystem('view');
    // initialize Twig environment
    $this->twig = new Twig_Environment($loader, array('cache' => $tmp_dir,
                'auto_reload' => true, 'debug' => true));
    $this->twig->addExtension(new Twig_Extensions_Extension_I18n());
    //ENABLES DUMP() method for easy and fun debugging!
    $this->twig->addExtension(new Twig_Extension_Debug());
    // used for setting the base href for the relative urls
    $this->twig->addGlobal("CurrentUrl", $_SERVER["REQUEST_URI"]);
    // setting the service name string from the config.inc
    $this->twig->addGlobal("ServiceName", SERVICE_NAME);
    
    // register a Twig filter for generating URLs for vocabulary resources (concepts and collections)
    $controller = $this; // for use by anonymous function below
    $urlFilter = new Twig_SimpleFilter('link_url', function ($uri, $vocab, $lang, $type='page') use ($controller) {
      // $vocab can either be null, a vocabulary id (string) or a Vocabulary object
      if ($vocab === null) {
        // target vocabulary is unknown, best bet is to link to the plain URI
        return $uri;
      } elseif (is_string($vocab)) {
        $vocid = $vocab;
        $vocab = $controller->model->getVocabulary($vocid);
      } else {
        $vocid = $vocab->getId();
      }
      // case 1: URI within vocabulary namespace: use only local name
      $localname = $vocab->getLocalName($uri);
      if ($localname != $uri) { // check that the prefix stripping worked
        return $controller->path_fix . "$vocid/$lang/$type/$localname";
      }
      
      // case 2: URI outside vocabulary namespace; pass the full URI as parameter instead
      return $controller->path_fix . "$vocid/$lang/$type/?uri=" . urlencode($uri);
    });
    $this->twig->addFilter($urlFilter);

    $tplDir = 'view';

    // iterate over all your templates
    foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($tplDir),
            RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
      // force compilation
      $path = explode('.', $file);
      $ext = end($path);
      if ($ext == 'twig')
        $this->twig->loadTemplate(str_replace($tplDir . '/', '', $file));
    }
  }

  /**
   * Guess the language of the user. Return a language string that is one
   * of the supported languages defined in the LANGUAGES constant, e.g. "fi".
   * @return string returns the language choice as a numeric string value
   */
  public function guessLanguage($vocab_id=null) {
    $LANGUAGES = explode(" ", LANGUAGES);

    // 1. select language based on ONKI_LANGUAGE cookie

    if (isset($_COOKIE['ONKI_LANGUAGE']))
      return $_COOKIE['ONKI_LANGUAGE'];
      
    // 2. if vocabulary given, select based on the default language of the vocabulary
    
    if ($vocab_id) {
      $vocab = $this->model->getVocabulary($vocab_id);
      return $vocab->getDefaultLanguage();
    }

    // 3. select language based on Accept-Language header
    // based on http://www.thefutureoftheweb.com/blog/use-accept-language-header

    $langs = array();

    if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
      // break up string into pieces (languages and q factors)
      preg_match_all(
              '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse);

      if (count($lang_parse[1])) {
        // create a list like "en" => 0.8
        $langs = array_combine($lang_parse[1], $lang_parse[4]);

        // set default to 1 for any without q factor
        foreach ($langs as $lang => $val) {
          if ($val === '')
            $langs[$lang] = 1;
        }

        // sort list based on value
        arsort($langs, SORT_NUMERIC);
      }
    }

    // look through sorted list and use first one that matches our languages
    foreach ($langs as $lang => $val) {
      foreach ($LANGUAGES as $uilang) {
        if (strpos($lang, $uilang) === 0)
          return $uilang;
      }
    }

    // show default site or prompt for language
    return $LANGUAGES[0];
  }

  /**
   * Loads and renders the view containing all the vocabularies.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeVocabularies($lang) {
    // set language parameters for gettext
    $this->setLanguageProperties($lang);
    // load template
    $template = $this->twig->loadTemplate('light.twig');
    // set template variables
    $requestUri = $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
    $vocabList = $this->model->getVocabularyList();
    // render template
    echo $template
            ->render(
                    array('vocabList' => $vocabList,
                        'path_fix' => $this->path_fix, 'front_page' => True,
                        'lang' => $lang, 'parts' => $this->parts, 'request_uri' => $this->request_uri));
  }

  /**
   * Invokes the concept page of a single concept in a specific vocabulary.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   * @param string $uri localname or uri of concept.
   */
  public function invokeVocabularyConcept($vocab_id, $lang, $uri) {
    $this->setLanguageProperties($lang);
    $template = $this->twig->loadTemplate('vocab-search-listing.twig');
    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id, 'request_uri' => $this->request_uri));
      exit();
    }
   
    $vocab_languages = $vocab->getLanguages();
    $lang_support = in_array($lang, $vocab_languages);
    $lang_msg = null; 
    // If not just pick a suitable replacement from the supported languages of the ontology
    if (!$lang_support) {
      if (in_array('fi', $vocab_languages)) {
        $lang = 'fi';
      } else if (in_array('en', $vocab_languages)) {
        $lang = 'en';
      } else if (in_array('sv', $vocab_languages)) {
        $lang = 'sv';
      }
      // translate this string here and now to avoid the wrong language issue in the template..
      $lang_msg = gettext("language_changed_message");
      $this->setLanguageProperties($lang);
    }

    $vocab = $this->model->getVocabulary($vocab_id);
    $uri = $vocab->getConceptURI($uri); // make sure it's a full URI
    $results = $vocab->getConceptInfo($uri);
    $crumbs = $this->model->getBreadCrumbs($vocab, $lang, $uri);
    echo $template->render(Array('search_results' => $results, 'vocab' => $vocab, 'vocab_id' => $vocab_id, 'path_fix' => $this->path_fix, 'lang' => $lang, 'request_uri' => $this->request_uri, 'bread_crumbs' => $crumbs['breadcrumbs'], 'combined' => $crumbs['combined'], 'lang_changed' => $lang_msg));
  }

  /**
   * Invokes the feedback page with information of the users current vocabulary.
   * @param string $vocab_id used for the default setting of the dropdown menu.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeFeedbackForm($lang, $vocab_id = null) {

    $template = $this->twig->loadTemplate('feedback.twig');
    $this->setLanguageProperties($lang);
    $vocabList = $this->model->getVocabularyList(false);
    try {
      $vocab = (isset($vocab_id)) ? $this->model->getVocabulary($vocab_id) : "";
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id,
          'vocabList' => $vocabList));
      return;
    }
    $feedback_sent = False;
    $feedback_msg = null;
    if (isset($_POST['message'])) {
      $feedback_sent = True;
      $feedback_msg = $_POST['message'];
    }
    $feedback_name = (isset($_POST['name'])) ? $_POST['name'] : null;
    $feedback_email = (isset($_POST['email'])) ? $_POST['email'] : null;
    $feedback_vocab = (isset($_POST['vocab'])) ? $_POST['vocab'] : null;

    if ($feedback_sent)
      $this->sendFeedback($feedback_msg, $feedback_name, $feedback_email, $feedback_vocab);

    if ($vocab_id == null)
      $vocab_id = 'Feedback';

    echo $template
            ->render(
                    array('path_fix' => $this->path_fix, 'lang' => $lang,
                        'vocab_id' => $vocab_id,
                        'vocab' => $vocab,
                        'vocabList' => $vocabList,
                        'feedback_sent' => $feedback_sent,
                        'parts' => $this->parts, 
                        'request_uri' => $this->request_uri
            ));
  }

  /**
   * Sends the user entered message through the php's mailer.
   * @param string $message only required parameter is the actual message.
   * @param string $fromName senders own name.
   * @param string $fromEmail senders email adress.
   * @param string $fromVocab which vocabulary is the feedback related to.
   */
  public function sendFeedback($message, $fromName = null, $fromEmail = null, $fromVocab = null) {
    $to = FEEDBACK_ADDRESS;
    if ($fromVocab != null)
      $message = 'Feedback from vocab: ' . strtoupper($fromVocab) . "<br />" . $message;
    $subject = "ONKI-light feedback";
    $headers = "MIME-Version: 1.0″ . '\r\n";
    $headers .= "Content-type: text/html; charset=iso-8859-1" . "\r\n";
    $headers .= "From: $fromName <$fromEmail>" . "\r\n" . 'X-Mailer: PHP/' . phpversion();
    $params = '-f osma.suominen@aalto.fi';
    mail($to, $subject, $message, $headers, $params) or die("Failure");
  }

  /**
   * Invokes the about page for the ONKI Light service.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeAboutPage($lang = 'en') {
    $template = $this->twig->loadTemplate('about.twig');
    $this->setLanguageProperties($lang);
    $vocab_id = 'About';
    $url = $_SERVER['HTTP_HOST'];
    echo $template
            ->render(array('path_fix' => $this->path_fix, 'lang' => $lang, 'vocab_id' => $vocab_id, 'server_instance' => $url, 'request_uri' => $this->request_uri));
  }
  
  /**
   * Invokes the search for concepts in all the availible ontologies.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeGlobalSearch($lang) {
    $template = $this->twig->loadTemplate('global-search-listing.twig');
    $this->setLanguageProperties($lang);

    $term = htmlspecialchars(isset($_GET['q']) ? $_GET['q'] : "");
    $lang_count = (isset($_GET['all']) && $_GET['all'] == '1') ? null : $lang;
    $offset = (isset($_GET['offset']) && is_numeric($_GET['offset']) && $_GET['offset'] >= 0) ? $_GET['offset'] : 0;
    if ($offset > 0) {
      $rest = 1;
      $template = $this->twig->loadTemplate('concept-info.twig');
    } else {
      $rest = null;
    }
    $term = trim($term); // surrounding whitespace is not considered significant
    $sterm = strpos($term, "*") === FALSE ? $term . "*" : $term; // default to prefix search
    $search_results = $this->model->searchConceptsAndInfo($sterm, null, $lang_count, $offset);
    $uri_parts = $_SERVER['REQUEST_URI'];

    echo $template->render(
            array('path_fix' => $this->path_fix, 'lang' => $lang,
                'search_results' => $search_results,
                'term' => $term,
                'lang_count' => $lang_count,
                'rest' => $rest, 'parts' => $this->parts, 'global_search' => True, 'uri_parts' => $uri_parts,
                'request_uri' => $this->request_uri

    ));
  }

  /**
   * Invokes the search for a single vocabs concepts.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeVocabularySearch($vocab_id, $lang) {
    $template = $this->twig->loadTemplate('vocab-search-listing.twig');
    $this->setLanguageProperties($lang);
    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id));
      return;
    }
    $term = htmlspecialchars(isset($_GET['q']) ? $_GET['q'] : "");
    $search_lang = (isset($_GET['lang'])) ? $_GET['lang'] : $lang;
    $offset = (isset($_GET['offset']) && is_numeric($_GET['offset']) && $_GET['offset'] >= 0) ? $_GET['offset'] : 0;
    if ($offset > 0) {
      $rest = 1;
      $template = $this->twig->loadTemplate('concept-info.twig');
    } else {
      $rest = null;
    }
    $term = trim($term); // surrounding whitespace is not considered significant
    $sterm = strpos($term, "*") === FALSE ? $term . "*" : $term; // default to prefix search
    try {
      $search_results = $this->model->searchConceptsAndInfo($sterm, $vocab_id, $search_lang, $offset, 20, $lang);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(
            array('path_fix' => $this->path_fix, 'lang' => $lang,
                'vocab_id' => $vocab_id,
                'vocab' => $vocab,
                'term' => $term,
                'lang_count' => $search_lang,
                'rest' => $rest, 'parts' => $this->parts));
    }
    $uri_parts = $_SERVER['REQUEST_URI'];
    echo $template->render(
            array('path_fix' => $this->path_fix, 'lang' => $lang,
                'vocab_id' => $vocab_id,
                'vocab' => $vocab,
                'search_results' => $search_results,
                'term' => $term,
                'lang_count' => $search_lang,
                'rest' => $rest, 'parts' => $this->parts, 'uri_parts' => $uri_parts, 
                'request_uri' => $this->request_uri

    ));
  }

  /**
   * Invokes the alphabetical listing for a specific vocabulary.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeAlphabeticalIndex($vocab_id, $lang, $letter = 'A') {
    $this->setLanguageProperties($lang);
    $template = $this->twig->loadTemplate('alphabetical-index.twig');

    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      $template = $this->twig->loadTemplate('vocab-search-listing.twig');
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id));
      return;
    }
    
    $vocab_languages = $vocab->getLanguages();
    $lang_support = in_array($lang, $vocab->getLanguages());
    $lang_msg = null; 
    // If not just pick a suitable replacement from the supported languages of the ontology
    if (!$lang_support) {
      if (in_array('fi', $vocab_languages)) {
        $lang = 'fi';
      } else if (in_array('en', $vocab_languages)) {
        $lang = 'en';
      } else if (in_array('sv', $vocab_languages)) {
        $lang = 'sv';
      }
      // translate this string here and now to avoid the wrong language issue in the template..
      $lang_msg = gettext("language_changed_message");
      $this->setLanguageProperties($lang);
    }

    $search_results = $this->model->searchConcepts($letter . "*", $vocab_id, $lang, null, null, null, 0, 0);
    $controller = $this; // for use by anonymous function below
    echo $template
            ->render(
                    array('path_fix' => $this->path_fix, 'lang' => $lang,
                        'vocab_id' => $vocab_id,
                        'vocab' => $vocab,
                        'search_results' => $search_results,
                        'search_letter' => $letter,
                        'letter' => $letter, 
                        'parts' => $this->parts, 
                        'request_uri' => $this->request_uri,
                        'lang_changed' => $lang_msg
            ));
  }
  
  public function invokeCollectionIndex($vocab_id, $lang) {
    $this->setLanguageProperties($lang);
    $template = $this->twig->loadTemplate('collection-index.twig');
    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      $template = $this->twig->loadTemplate('vocab-search-listing.twig');
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id));
      return;
    }
    $collections = $vocab->listCollections();
    echo $template
            ->render(
                    array('path_fix' => $this->path_fix, 'lang' => $lang,
                        'vocab_id' => $vocab_id,
                        'vocab' => $vocab,
                        'collections' => $collections,
                        'parts' => $this->parts, 
                        'request_uri' => $this->request_uri
            ));
  }

  public function invokeCollectionContents($vocab_id, $lang, $coll) {
    $this->setLanguageProperties($lang);
    $template = $this->twig->loadTemplate('collection-contents.twig');
    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      $template = $this->twig->loadTemplate('vocab-search-listing.twig');
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id));
      return;
    }
    $colluri = $vocab->getConceptURI($coll);
    $collections = $vocab->listCollections();
    $contents = $vocab->listCollectionContents($colluri);
    $collection_name = $collections[$colluri];
    echo $template
            ->render(
                    array('path_fix' => $this->path_fix, 'lang' => $lang,
                        'vocab_id' => $vocab_id,
                        'vocab' => $vocab,
                        'contents' => $contents,
                        'parts' => $this->parts, 
                        'label' => $collection_name, 
                        'request_uri' => $this->request_uri
            ));
  }

  /**
   * Loads and renders the view containing a specific vocabulary.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeVocabularyHome($vocab_id, $lang, $letter = 'A') {
    // set language parameters for gettext
    $this->setLanguageProperties($lang);

    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      header("HTTP/1.0 404 Not Found");
      $template = $this->twig->loadTemplate('vocab-search-listing.twig');
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
        'lang' => $lang, 'vocab_id' => $vocab_id)); 
      
      return;    
    }
    
    $vocab_stats = $this->model->getVocabulary($vocab_id)->getStatistics();

    // Testing if ui language choice is supported by the ontology
    $vocab_languages = $vocab->getLanguages();
    $lang_support = in_array($lang, $vocab->getLanguages());
    $lang_msg = null; 
    // If not just pick a suitable replacement from the supported languages of the ontology
    if (!$lang_support) {
      if (in_array('fi', $vocab_languages)) {
        $lang = 'fi';
      } else if (in_array('en', $vocab_languages)) {
        $lang = 'en';
      } else if (in_array('sv', $vocab_languages)) {
        $lang = 'sv';
      }
      // translate this string here and now to avoid the wrong language issue in the template..
      $lang_msg = gettext("language_changed_message");
      $this->setLanguageProperties($lang);
    }
    
    $search_results = $this->model->searchConcepts($letter . "*", $vocab_id, $lang, null, null, null, 0, 0);

    // load template
    $template = $this->twig->loadTemplate('vocab.twig');

    echo $template
            ->render(
                    array('path_fix' => $this->path_fix,
                        'lang' => $lang,
                        'vocab' => $vocab,
                        'parts' => $this->parts,
                        'vocab_id' => $vocab_id,
                        'lang_supported' => $lang_support, 
                        'request_uri' => $this->request_uri,
                        'search_results' => $search_results,
                        'vocab_stats' => $vocab_stats,
                        'lang_changed' => $lang_msg));
  }

  /**
   * Loads and renders the view containing a specific vocabulary.
   * @param string $vocab_id contains the name of the vocabulary in question.
   * @param string $lang language parameter eg. 'fi' for Finnish.
   */
  public function invokeVocabularyStats($vocab_id, $lang) {
    // set language parameters for gettext
    $this->setLanguageProperties($lang);
    // load template
    $template = $this->twig->loadTemplate('vocab-stats.twig');
    try {
      $vocab = $this->model->getVocabulary($vocab_id);
    } catch (Exception $e) {
      header("HTTP/1.0 404 Not Found");
      $template = $this->twig->loadTemplate('vocab-search-listing.twig');
      if (LOG_CAUGHT_EXCEPTIONS)
        error_log('Caught exception: ' . $e->getMessage());
      echo $template->render(array('path_fix' => $this->path_fix,
          'lang' => $lang, 'vocab_id' => $vocab_id));
      return;
    }

    $vocab_stats = $this->model->getVocabulary($vocab_id)->getStatistics();
    echo $template
            ->render(
                    array('path_fix' => $this->path_fix,
                        'lang' => $lang,
                        'vocab' => $vocab,
                        'vocab_id' => $vocab_id,
                        'parts' => $this->parts,
                        'vocab_stats' => $vocab_stats, 
                        'request_uri' => $this->request_uri));
  }

  /**
   * Invokes a very generic errorpage. 
   */
  public function invokeGenericErrorPage() {
    $lang = $this->guessLanguage();
    $this->setLanguageProperties($lang);
    header("HTTP/1.0 404 Not Found");
    $template = $this->twig->loadTemplate('error-page.twig');
    echo $template
            ->render(
                    array('path_fix' => $this->path_fix,
                        'lang' => $lang,
                        'parts' => $this->parts,
                        'requested_page' => $_SERVER['REQUEST_URI']));
  }


  /**
   * Invokes the stylesheet that defines vocabulary-specific colors.
   */
  public function invokeColorCSS() {
    $template = $this->twig->loadTemplate('color-stylesheet.twig');
    $vocabList = $this->model->getVocabularies();
    header("Content-type: text/css");
    echo $template
            ->render(
                    array('vocabList' => $vocabList));
  }

}
